﻿#include "pch.h"
#include <iostream>
#include "Console.h"

using namespace std;

// REFERENCE_TIME time units per second and per millisecond
#define REFTIMES_PER_SEC  10000000
#define REFTIMES_PER_MILLISEC  10000
#define SERVER_PORT 3779
#define MAX_BUF_LEN 16
/* 数据接收端口 */
#define DATA_RECEIVE_PORT 2233
/* 消息发送端口 */
#define MESSAGE_SEND_PORT 2234
/* 消息接收端口 */
#define MESSAGE_RECEIVE_PORT 2235
/* 数据缓冲区大小 */
#define DATA_BUFFER_SIZE 1024 * 4

#define EXIT_ON_ERROR(hres) if (FAILED(hres)) { goto Exit; }
#define SAFE_RELEASE(punk) if ((punk) != NULL) { (punk)->Release(); (punk) = NULL; }

const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient);
const IID IID_IAudioClient = __uuidof(IAudioClient);

BOOL bDone = FALSE;
IMMDeviceEnumerator *pEnumerator = NULL;
IMMDevice *pDevice = NULL;
IAudioClient *pAudioClient = NULL;
IAudioCaptureClient *pCaptureClient = NULL;
SOCKET sclient;
byte* soundData;
char ipBuffer[15];

void release()
{
	SAFE_RELEASE(pEnumerator)
	SAFE_RELEASE(pDevice)
	SAFE_RELEASE(pAudioClient)
	SAFE_RELEASE(pCaptureClient)

	closesocket(sclient);
	if (NULL != soundData)
		delete[] soundData;
}

BOOL WINAPI HandlerRoutine(DWORD dwCtrlType) {
	if (CTRL_CLOSE_EVENT == dwCtrlType) {
		// 控制台将要被关闭，这里添加你的处理代码 ...
		release();
	}
	return TRUE;
}

HRESULT RecordAudioStream(const char * ip)
{
	int dataSize = 0;
	UINT32 packetLength = 0;
	UINT32 numFramesAvailable;
	HRESULT hr;
	REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC;
	WAVEFORMATEX *pwfx = NULL;
	BYTE *pData;
	DWORD flags;

	if (FALSE == initSocket(ip)) {
		return 0;
	}

	// 设置音频格式
	WAVEFORMATEX waveFormat;
	initWaveFormat(waveFormat);

	pwfx = &waveFormat;
	soundData = new byte[DATA_BUFFER_SIZE];

	hr = CoCreateInstance(CLSID_MMDeviceEnumerator,
		NULL,
		CLSCTX_ALL,
		IID_IMMDeviceEnumerator,
		(void**)&pEnumerator);
	EXIT_ON_ERROR(hr)

	hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice);
	EXIT_ON_ERROR(hr)

	hr = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient);
	EXIT_ON_ERROR(hr)

	hr = pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED,
		AUDCLNT_STREAMFLAGS_LOOPBACK,
		hnsRequestedDuration,
		0,
		pwfx,
		NULL);
	EXIT_ON_ERROR(hr)

	hr = pAudioClient->GetService(IID_IAudioCaptureClient, (void**)&pCaptureClient);
	EXIT_ON_ERROR(hr)

	hr = pAudioClient->Start();  // Start recording.
	EXIT_ON_ERROR(hr)


	// Each loop fills about half of the shared buffer.
	while (bDone == FALSE)
	{
		Sleep(1);
		hr = pCaptureClient->GetNextPacketSize(&packetLength);
		EXIT_ON_ERROR(hr)

		while (packetLength != 0)
		{
			// Get the available data in the shared buffer.
			hr = pCaptureClient->GetBuffer(&pData, &numFramesAvailable, &flags, NULL, NULL);
			if (FAILED(hr))
			{
				Sleep(100);

				hr = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient);
				EXIT_ON_ERROR(hr)

				hr = pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED,
					AUDCLNT_STREAMFLAGS_LOOPBACK,
					hnsRequestedDuration,
					0,
					pwfx,
					NULL);
				EXIT_ON_ERROR(hr)

				hr = pAudioClient->GetService(IID_IAudioCaptureClient, (void**)&pCaptureClient);
				EXIT_ON_ERROR(hr)

				hr = pAudioClient->Start();  // Start recording.
				EXIT_ON_ERROR(hr)

				break;
			}

			if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
			{
				pData = NULL;  // Tell CopyData to write silence.
				continue;
			}

			dataSize = numFramesAvailable * 4;

			// 将数据块的大小写入前四个字节
			soundData[0] = (BYTE)(dataSize & 0xFF);
			soundData[1] = (BYTE)((dataSize >> 8) & 0xFF);
			soundData[2] = (BYTE)((dataSize >> 16) & 0xFF);
			soundData[3] = (BYTE)((dataSize >> 24) & 0xFF);

			::memcpy(&soundData[4], pData, dataSize);

			send(sclient, (char *)soundData, dataSize + 4, 0);

			hr = pCaptureClient->ReleaseBuffer(numFramesAvailable);
			EXIT_ON_ERROR(hr)

			hr = pCaptureClient->GetNextPacketSize(&packetLength);
			EXIT_ON_ERROR(hr)
		}
	}

	hr = pAudioClient->Stop();  // Stop recording.
	EXIT_ON_ERROR(hr)

Exit:
	release();
	return hr;
}

void initWaveFormat(WAVEFORMATEX &waveFormat)
{
	waveFormat.wFormatTag = WAVE_FORMAT_PCM;
	waveFormat.nChannels = 2;
	waveFormat.nSamplesPerSec = 44100;
	waveFormat.nAvgBytesPerSec = 2 * 16 * 44100 / 8;
	waveFormat.wBitsPerSample = 16;
	waveFormat.nBlockAlign = 4;
	waveFormat.cbSize = 0;
}

const BOOL initSocket(const char * ip)
{
	// 初始化Socket
	WORD sockVersion = MAKEWORD(2, 2);
	WSADATA wsadata;
	if (WSAStartup(sockVersion, &wsadata) != 0)
	{
		return FALSE;
	}

	sclient = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (sclient == INVALID_SOCKET)
	{
		printf("invalid socket!");
		return FALSE;
	}
	sockaddr_in serAddr;
	serAddr.sin_family = AF_INET;
	serAddr.sin_port = htons(DATA_RECEIVE_PORT);
	serAddr.sin_addr.S_un.S_addr = inet_addr(ip);
	if (connect(sclient, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR)
	{
		//连接失败 
		printf("connect error !");
		closesocket(sclient);
		WSACleanup();
		return FALSE;
	}

	return TRUE;
}

void sendMessage(const char * message)
{
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	// 启动socket api
	wVersionRequested = MAKEWORD(2, 2);
	err = WSAStartup(wVersionRequested, &wsaData);
	if (err != 0)
	{
		return;
	}

	if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
	{
		WSACleanup();
		return;
	}

	// 创建socket
	SOCKET connect_socket;
	connect_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (INVALID_SOCKET == connect_socket)
	{
		err = WSAGetLastError();
		printf("error! error code is %d/n", err);
		return;
	}

	SOCKADDR_IN sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(MESSAGE_SEND_PORT);
	sin.sin_addr.s_addr = INADDR_BROADCAST;
	//sin.sin_addr.s_addr = inet_addr("255.255.255.255");//设置广播地址,window下用这种形式方可

	bool bOpt = true;
	//设置该套接字为广播类型
	setsockopt(connect_socket, SOL_SOCKET, SO_BROADCAST, (char*)&bOpt, sizeof(bOpt));
	int nAddrLen = sizeof(SOCKADDR);

	char buff[MAX_BUF_LEN];
	::memset(buff, 0, MAX_BUF_LEN);
	::memcpy(buff, message, strlen(message));

	//cout << "length:" << strlen(message) << endl;
	for (auto i = 0; i < 10; i++)
	{
		// 发送数据
		int nSendSize = (int)sendto(connect_socket, buff, strlen(buff), 0, (SOCKADDR*)&sin, nAddrLen);
		if (SOCKET_ERROR == nSendSize)
		{
			err = WSAGetLastError();
			printf("error!, error code is %d/n", err);
			return;
		}

		Sleep(10);
	}
}

void receiveMessage() {
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	// 启动socket api
	wVersionRequested = MAKEWORD(2, 2);
	err = WSAStartup(wVersionRequested, &wsaData);
	if (err != 0)
	{
		return;
	}

	if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
	{
		WSACleanup();
		return;
	}

	// 创建socket
	SOCKET connect_socket;
	connect_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (INVALID_SOCKET == connect_socket)
	{
		err = WSAGetLastError();
		printf("error! error code is %d/n", err);
		return;
	}

	// 用来绑定套接字
	SOCKADDR_IN sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(MESSAGE_RECEIVE_PORT);
	sin.sin_addr.s_addr = 0;

	// 用来从网络上的广播地址接收数据
	SOCKADDR_IN sin_from;
	sin_from.sin_family = AF_INET;
	sin_from.sin_port = htons(MESSAGE_RECEIVE_PORT);
	sin_from.sin_addr.s_addr = INADDR_BROADCAST;

	// 设置该套接字为广播类型，
	bool bOpt = true;
	setsockopt(connect_socket, SOL_SOCKET, SO_BROADCAST, (char*)&bOpt, sizeof(bOpt));

	// 绑定套接字
	err = ::bind(connect_socket, (SOCKADDR*)&sin, sizeof(SOCKADDR));
	if (SOCKET_ERROR == err)
	{
		err = WSAGetLastError();
		printf("error! error code is %d/n", err);
		return;
	}

	int nAddrLen = sizeof(SOCKADDR);
	char buff[MAX_BUF_LEN] = "";
	int nLoop = 0;
	while (1)
	{
		// 接收数据
		::memset(buff, 0, MAX_BUF_LEN);
		int nSendSize = recvfrom(connect_socket, buff, MAX_BUF_LEN, 0, (SOCKADDR*)&sin_from, &nAddrLen);
		if (SOCKET_ERROR == nSendSize)
		{
			err = WSAGetLastError();
			printf("error! error code is %d/n", err);
			return;
		}

		buff[nSendSize] = '\0';
		if (0 == strncmp("sendIP", buff, 6)) {
			printf("Recv: %s\n", buff);
			continue;
		}
		printf("Recv: %s\n", buff);
		::memset(ipBuffer, 0, sizeof(ipBuffer));
		::memcpy(ipBuffer, buff, nSendSize);
		return;
	}
}

int main()
{
	char cmd[10];
	char exit[] = "exit";

	cout << "enter exit to exit!" << endl;
	cout << "wating for the IP of phone......" << endl;

	//thread sendMessageThread([] {
	//	// to-do 获取本机IP
	//	sendMessage("sendIP to :"); 
	//});

	receiveMessage();

	printf("ip of phone:%s\n", ipBuffer);
	if (0 == strncmp("error", ipBuffer, 4)) {
		cout << "there is something error!" << endl;
		return 0;
	}

	SetConsoleCtrlHandler(HandlerRoutine, TRUE);
	thread t([] {
		CoInitialize(NULL);
		RecordAudioStream(ipBuffer);
		CoUninitialize();
	});

	while (true)
	{
		cin >> cmd;
		if (0 == strncmp(exit, cmd, 4)) {
			cout << "program exited!" << endl;
			bDone = TRUE;
			if (t.joinable())
				t.join();
			break;
		}
		cout << 1;
	}

	return 0;
}
